#include <unistd.h>
#include "lwip/sockets.h"
#include "cmsis_os2.h"
#include "lwip/ip_addr.h"
#include "lwip/netifapi.h"
#include "lwip/api_shell.h"
#include "wifi_hotspot.h"
#include "wifi_device.h"
#include "wifi_device_config.h"
#include "wifi_hotspot_config.h"
#include "wifi_error_code.h"

#include "utils_file.h"
#include "wlan.h"
#include "ssd1306.h"

#include "http_wifi_config.h"

#if 0
void OnWifiScanStateChanged(int state, int size);
#endif
void OnWifiConnectionChanged(int state, WifiLinkedInfo *info);

WifiEvent g_staEventHandler = {
    .OnWifiConnectionChanged = OnWifiConnectionChanged,
    .OnWifiScanStateChanged = NULL,
    .OnHotspotStateChanged = NULL,
    .OnHotspotStaJoin = NULL,
    .OnHotspotStaLeave = NULL};

// 强烈怀疑漏开了接口
WifiErrorCode GetHotspotInterfaceName(char *result, int size);

volatile enum PBWlanMode g_currPBWlanMode = PB_WLAN_MODE_SHUTDOWN;

static HotspotConfig g_wifiConfig = {
    .ssid = "MyTestAP",
    .band = HOTSPOT_BAND_TYPE_2G,
    .securityType = WIFI_SEC_TYPE_PSK,
    .channelNum = 6,
    .preSharedKey = "01234567"};

#define NETIF_NAME "ap0"

static int32_t ShutdownWlan(void)
{
    printf("%s\n", __func__);
    if (g_currPBWlanMode == PB_WLAN_MODE_SHUTDOWN)
    {
        return 0;
    }
    else if (g_currPBWlanMode == PB_WLAN_MODE_CONFIG)
    {
        char ifName[32];
        WifiErrorCode errCode = GetHotspotInterfaceName(ifName, 32);
        if (errCode != WIFI_SUCCESS)
        {
            printf("%s:Failed to get ifName!ret=%d\n", __func__, errCode);
            return -1;
        }

        struct netif *lwip_netif = netifapi_netif_find(ifName);
        if (lwip_netif == NULL)
        {
            printf("%s:netif not found!", __func__);
            return -1;
        }
        netifapi_dhcps_stop(lwip_netif);

        errCode = DisableHotspot();
        if (errCode != WIFI_SUCCESS)
        {
            printf("%s:shutdown ap failed!", __func__);
        }
        g_currPBWlanMode = PB_WLAN_MODE_SHUTDOWN;
        return 0;
    }
    else if (g_currPBWlanMode == PB_WLAN_MODE_RUNNING)
    {
        g_currPBWlanMode = PB_WLAN_MODE_SHUTDOWN;
        return 0;
    }
    else
    {
        printf("%s:bad wifi status!status=%d", __func__, g_currPBWlanMode);
        g_currPBWlanMode = PB_WLAN_MODE_SHUTDOWN;
        return 0;
    }
}

static void ConfigModeTask(void *arg);
static void RunningModeTask(void *arg);

void PBWlanSwitchTo(enum PBWlanMode mode)
{
    osThreadFunc_t targetTask = NULL;
    if (mode == PB_WLAN_MODE_CONFIG)
    {
        targetTask = ConfigModeTask;
    }
    else if (mode == PB_WLAN_MODE_RUNNING)
    {
        targetTask = RunningModeTask;
    }
    else
    {
        printf("%s:Bad input mode.mode=%d", __func__, mode);
        return;
    }

    osThreadAttr_t attr;
    attr.name = "WifiConfigTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 30480;
    attr.priority = osPriorityNormal;

    if (osThreadNew(targetTask, NULL, &attr) == NULL)
    {
        printf("%s: Falied to create WifiConfigTask!\n");
    }
}

#if 0
static void WifiScanStateTask(void)
{
    WifiScanInfo result[WIFI_SCAN_HOTSPOT_LIMIT];
    unsigned int resultSize = WIFI_SCAN_HOTSPOT_LIMIT;
    WifiErrorCode errCode = GetScanInfoList(result, &resultSize);
    if (errCode != WIFI_SUCCESS)
    {
        printf("%s:GetScanInfoList failed!ret=%d\r\n", __func__, errCode);
        return;
    }

#ifdef __DEBUG
    for (unsigned int i = 0; i < resultSize; i++)
    {
        printf("%s:scanned ssid %s\r\n", __func__, result[i].ssid);
    }
#endif

    if(resultSize == 0) {
        printf("%s:Target ssid not found!\r\n", __func__);
        return;
    }
}

void OnWifiScanStateChanged(int state, int size)
{
    (void)size;
    if (state != WIFI_STATE_AVALIABLE)
    {
        return;
    }

    osThreadAttr_t attr;
    attr.name = "WifiScanStateTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 0x6000;
    attr.priority = osPriorityNormal;
    if (osThreadNew((osThreadFunc_t)WifiScanStateTask, NULL, &attr) == NULL)
    {
        printf("ScanStateChanged:create task fail!\n");
    }
}
#endif

void OnWifiConnectionChanged(int state, WifiLinkedInfo *info)
{
    if (state == WIFI_STATE_AVALIABLE)
    {
        printf("WIFI connected.SSID=%s\n", info->ssid);
    }
    else if (state == WIFI_STATE_NOT_AVALIABLE)
    {
        printf("WIFI disconnected.Reason=%d\n", info->disconnectedReason);
    }
}

static void RunningModeTask(void *arg)
{
    (void)arg;
    int32_t ret = ShutdownWlan();
    if (ret != 0)
    {
        return;
    }
    // TODO: not supportted yet
    WifiErrorCode errCode = EnableWifi();
    if (errCode != WIFI_SUCCESS)
    {
        printf("%s:enable wifi failed!ret=%d\n", __func__, errCode);
        return;
    }

    errCode = RegisterWifiEvent(&g_staEventHandler);
    if (errCode != WIFI_SUCCESS)
    {
        printf("%s:RegisterWifiEvent failed!ret=%d\n", __func__, errCode);
        return;
    }

    /*
    WifiScanParams params;
    memcpy(params.ssid, g_wifiConfig.ssid, WIFI_MAX_SSID_LEN);
    params.ssidLen = strlen(params.ssid);
    params.scanType = WIFI_SSID_SCAN;

    printf("%s:SSID=%s\r\n", __func__, params.ssid);

    errCode = AdvanceScan(&params);
    if (errCode != WIFI_SUCCESS)
    {
        printf("%s:scan failed!ret=%d", __func__, errCode);
        return;
    }
    */

    WifiDeviceConfig config;
    memset(&config, 0, sizeof(config));
    memcpy(config.ssid, g_wifiConfig.ssid, WIFI_MAX_SSID_LEN);
    //memcpy(config.bssid, result[0].bssid, WIFI_MAC_LEN);
    memcpy(config.preSharedKey, g_wifiConfig.preSharedKey, WIFI_MAX_KEY_LEN);
    config.securityType = WIFI_SEC_TYPE_PSK; //result[0].securityType;
    config.freq = 0;                         //result[0].frequency;
    config.wapiPskType = WIFI_PSK_TYPE_ASCII;
    int netID = 0;
    errCode = AddDeviceConfig(&config, &netID);
    if (errCode != WIFI_SUCCESS)
    {
        printf("%s:add device failed!ret=%d\n", __func__, errCode);
        return;
    }
    printf("%s:netid = %d\r\n", __func__, netID);

    errCode = ConnectTo(netID);
    if (errCode != WIFI_SUCCESS)
    {
        printf("%s:connect to wifi failed!ret=%d\n", __func__, errCode);
        return;
    }

    struct netif *iface = netifapi_netif_find("wlan0");
    if (iface == NULL)
    {
        printf("wlan0 is NULL\n");
        return;
    }
    ret = netifapi_dhcp_start(iface);
    printf("netifapi_dhcp_start: %d\r\n", ret);

    osDelay(200); // wait DHCP server give me IP

    char ipAddr[] = "255.255.255.255";
    char ifName[] = "wlan0";

    lwip_get_ipv4(ipAddr, sizeof(ipAddr), (unsigned char *)ifName);

    ssd1306_Fill(Black);
    ssd1306_SetCursor(0, 0);
    ssd1306_DrawString("IP:", Font_7x10, White);
    ssd1306_DrawString(ipAddr, Font_7x10, White);
    ssd1306_UpdateScreen();

    return;
}

static void ConfigModeTask(void *arg)
{
    (void)arg;
    int32_t ret = ShutdownWlan();
    if (ret != 0)
    {
        return;
    }

    WifiErrorCode errCode = SetHotspotConfig(&g_wifiConfig);
    if (errCode != WIFI_SUCCESS)
    {
        printf("SetHotspotConfig failed!ret=%d", errCode);
        return;
    }

    errCode = EnableHotspot();
    if (errCode != WIFI_SUCCESS)
    {
        printf("Open AP failed!ret=%d", errCode);
        return;
    }

    char ifName[32];
    errCode = GetHotspotInterfaceName(ifName, 32);
    if (errCode != WIFI_SUCCESS)
    {
        printf("%s:Failed to get ifName!ret=%d\n", __func__, errCode);
        return;
    }

    /* acquire netif for IP operation */
    struct netif *lwip_netif = netifapi_netif_find(ifName);
    if (lwip_netif == NULL)
    {
        printf("%s: get netif failed\n", __FUNCTION__);
        return;
    }

    ip4_addr_t gw;
    ip4_addr_t ipaddr;
    ip4_addr_t mask;

    IP4_ADDR(&gw, 192, 168, 1, 1);
    IP4_ADDR(&ipaddr, 192, 168, 1, 1);
    IP4_ADDR(&mask, 255, 255, 255, 0);
    netifapi_netif_set_addr(lwip_netif, &ipaddr, &mask, &gw);

    netifapi_dhcps_start(lwip_netif, 0, 0);

    RunWifiConfigServer();

    ShutdownWlan();

    PBWlanSwitchTo(PB_WLAN_MODE_RUNNING);
    return;
}

#define CONFIG_FILE "/wlan_config.conf"
#define CONFIG_SPLITER "\r\n"

static uint8_t GetCurrentMode(void)
{
    unsigned int fileSize = 0;
    int errCode = UtilsFileStat(CONFIG_FILE, &fileSize);
    if (errCode == -1)
    {
        printf("File not exists!ret=%d\n", errCode);
        return PB_WLAN_MODE_CONFIG;
    }
    else
    {
        int fd = UtilsFileOpen(CONFIG_FILE, O_RDWR_FS, 0);
        if (fd < 0)
        {
            printf("%s:Open file failed!ret=%d", __func__, fd);
            return PB_WLAN_MODE_CONFIG;
        }
        char buff[WIFI_MAX_SSID_LEN + WIFI_MAX_KEY_LEN + strlen(CONFIG_SPLITER)];
        memset(buff, 0, sizeof(buff));
        int readSize = UtilsFileRead(fd, buff, sizeof(buff));
        if (readSize <= 0 || (unsigned int)readSize > sizeof(buff))
        {
            printf("Bad config file!");
            UtilsFileClose(fd);
            return PB_WLAN_MODE_CONFIG;
        }
        uint8_t startMode;
        char *spliter = strstr(buff, CONFIG_SPLITER);
        if (spliter == NULL)
        {
            printf("Bad config file format!");
            startMode = PB_WLAN_MODE_CONFIG;
        }
        else
        {
            memcpy_s(g_wifiConfig.ssid, WIFI_MAX_SSID_LEN, buff, spliter - buff);
            g_wifiConfig.ssid[(spliter - buff > 0 && spliter - buff < WIFI_MAX_SSID_LEN) ? spliter - buff : WIFI_MAX_SSID_LEN - 1] = '\0';
            strcpy_s(g_wifiConfig.preSharedKey, WIFI_MAX_KEY_LEN, spliter + strlen(CONFIG_SPLITER));

            startMode = PB_WLAN_MODE_RUNNING;
        }
        UtilsFileClose(fd);
        return startMode;
    }
}

void InitWLan(void)
{
    uint8_t startMode = GetCurrentMode();

    if (startMode == PB_WLAN_MODE_CONFIG)
    {
        strcpy_s(g_wifiConfig.ssid, WIFI_MAX_SSID_LEN, "WiFiIot_33");
        strcpy_s(g_wifiConfig.preSharedKey, WIFI_MAX_KEY_LEN, "12345678");

        ssd1306_SetCursor(0, 0);
        ssd1306_DrawString("Connect this AP", Font_7x10, White);
        ssd1306_SetCursor(0, 10);
        ssd1306_DrawString("to Config network", Font_7x10, White);
        ssd1306_SetCursor(0, 20);
        ssd1306_DrawString("SSID:", Font_7x10, White);
        ssd1306_DrawString(g_wifiConfig.ssid, Font_7x10, White);
        ssd1306_SetCursor(0, 30);
        ssd1306_DrawString("PW:  ", Font_7x10, White);
        ssd1306_DrawString(g_wifiConfig.preSharedKey, Font_7x10, White);
        ssd1306_SetCursor(0, 40);
        ssd1306_DrawString("Web IP:192.168.1.1", Font_7x10, White);
        ssd1306_UpdateScreen();
        PBWlanSwitchTo(startMode);
    }
    else if (startMode == PB_WLAN_MODE_RUNNING)
    {
        ssd1306_Fill(Black);
        ssd1306_SetCursor(0, 0);
        ssd1306_DrawString("Connecting...", Font_7x10, White);
        ssd1306_UpdateScreen();

        PBWlanSwitchTo(startMode);
    }
}

void SaveWifiSTAConfig(char *strSSID, char *psk)
{
    int fd = UtilsFileOpen(CONFIG_FILE, O_RDWR_FS | O_CREAT_FS, 0);
    if (fd < 0)
    {
        printf("%s:Open file failed!ret=%d", __func__, fd);
        return;
    }

    UtilsFileWrite(fd, strSSID, strlen(strSSID));
    UtilsFileWrite(fd, "\r\n", strlen("\r\n"));
    UtilsFileWrite(fd, psk, strlen(psk));
    UtilsFileWrite(fd, "\0", 1);

    UtilsFileClose(fd);
}